package com.gitee.apanlh.util.reflection;

import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.Empty;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.cache.local.Cache;
import com.gitee.apanlh.util.cache.local.CacheUtils;
import com.gitee.apanlh.util.encode.StrEncodeUtils;
import com.gitee.apanlh.util.file.FileUtils;
import com.gitee.apanlh.util.setting.ResourceType;
import com.gitee.apanlh.util.setting.ResourceUtils;
import com.gitee.apanlh.util.thread.StackUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;

import java.io.File;
import java.net.URL;
import java.util.Collection;
import java.util.List;
import java.util.jar.JarFile;

/**	
 * 	类工具类
 * 	
 * 	@author Pan
 */
public class ClassUtils {
	
	/** 类缓存 */
	private static final Cache<String, Class<?>> CLASS_CACHE 	   = CacheUtils.cache(32);
	private static final Cache<Object[], Class<?>[]> CLASS_ARRAY_CACHE = CacheUtils.cache(32);
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private ClassUtils() {
		//	不允许外部实例
		super();
	}

	/**
	 * 	获取全限定名
	 * 	
	 * 	@author Pan
	 *  @param  <T>     数据类型
	 * 	@param 	obj		对象
	 * 	@return	String
	 */
	public static <T> String getName(T obj) {
		return getName(obj.getClass());
	}
	
	/**	
	 * 	获取全限定名
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz	类
	 * 	@return	String
	 */
	public static String getName(Class<?> clazz) {
		return clazz.getName();
	}
	
	/**
	 * 	获取全限定名
	 * 	<br>假设separator值为[/]
	 * 	<br>{@code 自定义替换部分:原有com.xxx.xxx.xx -> 替换后com/xxx/xxx/xx}
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz				类
	 * 	@param 	replaceSeparator	替换分隔符
	 * 	@return	String
	 */
	public static String getNameOfReplaceSeparator(Class<?> clazz, String replaceSeparator) {
		return StringUtils.replace(getName(clazz), ".", replaceSeparator);
	}
	
	/**
	 * 	获取全限定名
	 * 	获取全限定名
	 * 	<br>假设替换分隔符值为[/]
	 * 	<br>{@code 原有com.xxx.xxx.xx -> 替换后com/xxx/xxx/xx}
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	obj					对象
	 * 	@param 	replaceSeparator	替换分隔符
	 * 	@return	String
	 */
	public static <T> String getNameOfReplaceSeparator(T obj, String replaceSeparator) {
		if (obj == null) {
			return null;
		}
		return getNameOfReplaceSeparator(obj.getClass(), replaceSeparator);
	}
	
	/**
	 * 	获取包名
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	obj		对象
	 * 	@return	String
	 */
	public static <T> String getPackageName(T obj) {
		return getPackageName(obj.getClass());
	}
	
	/**	
	 * 	获取包名
	 * 
	 * 	@author Pan
	 * 	@param 	clazz	类
	 * 	@return	String
	 */
	public static String getPackageName(Class<?> clazz) {
		return clazz.getPackage().getName();
	}
	
	/**	
	 * 	获取Class名称
	 * 	<br>非全限定名
	 * 
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	obj		对象
	 * 	@return	String
	 */
	public static <T> String getSimpleName(T obj) {
		return getSimpleName(obj.getClass());
	}
	
	/**	
	 * 	获取Class名称
	 * 	<br>非全限定名
	 * 	<br>自定义首字母是否小写
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	obj				对象
	 * 	@param 	isLowerFirst	是否首字母小写
	 * 	@return	String
	 */
	public static <T> String getSimpleName(T obj, boolean isLowerFirst) {
		return getSimpleName(obj.getClass(), isLowerFirst);
	}
	
	/**	
	 * 	获取Class名称
	 *  <br>非全限定名
	 *  
	 * 	@author Pan
	 * 	@param 	clazz	类
	 * 	@return	String
	 */
	public static String getSimpleName(Class<?> clazz) {
		return getSimpleName(clazz, false);
	}
	
	/**	
	 * 	获取Class名称
	 *  <br>自定义首字母是否小写
	 *  
	 * 	@author Pan	
	 * 	@param 	clazz			类
	 * 	@param 	isLowerFirst	是否首字母小写
	 * 	@return	String
	 */
	public static String getSimpleName(Class<?> clazz, boolean isLowerFirst) {
		String simpleName = clazz.getSimpleName();
		return isLowerFirst ? StringUtils.lowerFirst(simpleName) : simpleName;
	}
	
	/**	
	 * 	获取代理原始类
	 * 
	 * 	@author Pan
	 *	@param 	clazz		类
	 * 	@param  proxyTag	代理标识符
	 * 	@return	Class
	 */
	public static Class<?> getProxyClass(Class<?> clazz, String proxyTag) {
		if (ValidParam.isEmpty(proxyTag)) {
			return clazz;
		}
		
		if (clazz.getName().contains(proxyTag)) {
			Class<?> superclass = clazz.getSuperclass();
			if (superclass != null && superclass != Object.class) {
				clazz = superclass;
			}
		}
		return clazz;
	}

	/**	
	 * 	根据对象获取Class
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param	obj		对象
	 * 	@return	Class
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getClass(T obj) {
		if (obj == null) {
			return null;
		}
		return (Class<T>) obj.getClass();
	}
	
	/**	
	 * 	在collection中返回首次找到的类
	 * 
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	collection	集合
	 * 	@return	Class
	 * 	@throws ArrayIndexOutOfBoundsException 如果没有找到时出现此异常
	 */
	public static <T> Class<?> getClass(Collection<T> collection) {
		Assert.isNotNull(collection);
		
	    for (T t : collection) {
	        if (t != null) {
	            return t.getClass();
	        }
	    }
	    throw new ArrayIndexOutOfBoundsException("get class error cause: not found class");
	}
	
	/**	
	 * 	获取当前调用者的Class
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public static String getClassStr() {
		return StackUtils.getCallClassName();
	}
	
	/**	
	 * 	根据当前调用者的获取类
	 * 	
	 * 	@author Pan
	 * 	@return	Class
	 */
	public static Class<?> getClassOfCall() {
		return forName(getClassStr());
	}

	/**	
	 * 	根据数组获取相对应的Class类
	 * 	
	 * 	@author Pan
	 * 	@param 	objects	对象数组
	 * 	@return	Class
	 */
	public static Class<?>[] getClasses(Object[] objects) {
		if (ValidParam.isEmpty(objects)) {
			return Empty.arrayClass();
		}
		
		return CLASS_ARRAY_CACHE.get(objects, () -> {
			Class<?>[] classes = new Class<?>[objects.length];
			for (int i = 0; i < objects.length; i++) {
				classes[i] = objects[i].getClass();
			}
			return classes;
		});
	}
	
	/**	
	 * 	根据List获取Class类
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	list	集合
	 * 	@return	Class
	 */
	public static <T> Class<?>[] getClasses(List<T> list) {
		if (ValidParam.isEmpty(list)) {
			return Empty.arrayClass();
		}
		return getClasses(list.toArray());
	}
	
	/**
	 * 	根据包名地址获取相对应的所有Class文件
	 * 	<br>自定义搜索级别
	 * 	
	 * 	@author Pan
	 * 	@param 	resourceLocation	资源地址
	 * 	@param 	packagePath			包名地址(com/xxx/xxx)
	 * 	@param 	multiLevel			true搜索多层级别,false搜索单层级别
	 * 	@return List
	 */
	public static List<Class<?>> getClassesOfJarPath(String resourceLocation, String packagePath, boolean multiLevel) {
		return getClassesOfJarPath(ResourceUtils.getUrl(resourceLocation), packagePath, multiLevel);
	}
	
	/**
	 * 	根据包名地址获取相对应的所有Class文件
	 * 	<br>自定义搜索级别
	 * 	
	 * 	@author Pan
	 * 	@param 	url				资源
	 * 	@param 	packagePath		包名地址(com/xxx/xxx)
	 * 	@param 	multiLevel		true搜索多层级别,false搜索单层级别
	 * 	@return List
	 */
	public static List<Class<?>> getClassesOfJarPath(URL url, String packagePath, boolean multiLevel) {
		JarFile jarFile = ResourceUtils.getJarFile(url);
		if (jarFile == null) {
			return Empty.list();
		}
		
		List<Class<?>> list = CollUtils.newArrayList();
		
		//	后期修改为 包下全扫描 非单个包（增加文件夹目录判断）
		IteratorUtils.array(jarFile.entries(), jarEntry -> {
			String fileName = jarEntry.getName();
			
			if (fileName.startsWith(packagePath) && FileUtils.isClassFile(fileName)) {
				//	多层包
				if (multiLevel) {
					list.add(ClassUtils.forName(fileName.replace("/", ".").replace(FileUtils.CLASS_SUFFIX, "")));
				} else {
					//	单层包
					if (fileName.indexOf('/', packagePath.length()) <= 0) {
						list.add(ClassUtils.forName(fileName.replace("/", ".").replace(FileUtils.CLASS_SUFFIX, "")));
					}
				}
			}
		});
		return list;
	}
	
	/**	
	 * 	根据包名地址获取相对应的所有Class文件
	 * 	<br>自定义搜索级别
	 * 	
	 * 	@author Pan
	 * 	@param 	filePath		文件绝对路径地址
	 * 	@param 	packagePath		包名地址(com.xxx.xxx)
	 * 	@param 	multiLevel		true搜索多层级别,false搜索单层级别
	 * 	@return List
	 */
	public static List<Class<?>> getClassesOfFilePath(String filePath, String packagePath, boolean multiLevel) {
		return getClassesOfFilePath(filePath, packagePath, CollUtils.newArrayList(), multiLevel);
	}
	
	/**	
	 * 	根据包名地址获取相对应的所有Class文件
	 * 	<br>自定义搜索级别
	 * 	<br>递归搜索
	 * 	
	 * 	@author Pan
	 * 	@param 	filePath		文件绝对路径地址
	 * 	@param 	packagePath		包名地址(com.xxx.xxx)
	 * 	@param 	list			集合
	 * 	@param 	multiLevel		true搜索多层级别,false搜索单层级别
	 * 	@return List
	 */
	private static List<Class<?>> getClassesOfFilePath(String filePath, String packagePath, List<Class<?>> list, boolean multiLevel) {
		//  获取所有文件
		File[] files = new File(StrEncodeUtils.urlDecode(filePath)).listFiles();
		
		IteratorUtils.array(files, file -> {
			String fileName = file.getName();
			
			//	递归
			if (multiLevel && file.isDirectory()) {
				getClassesOfFilePath(file.getAbsolutePath(), StringUtils.append(packagePath, ".", file.getName()), list, multiLevel);
			}
			//	根据全限定名加载类
			if (FileUtils.isClassFile(file)) {
				list.add(ClassUtils.forName(StringUtils.append(packagePath, ".", fileName.substring(0, fileName.length() - 6))));
			}
		});
		return list;
	}

	/**
	 * 	获取默认ClassLoader
	 * 	<br>如果当前线程类加载器无法获取时将获取JVM类加载器
	 * 	
	 * 	@author Pan
	 * 	@return	ClassLoader
	 */
	public static ClassLoader getClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Exception ex) {
			//	继续执行获取
		}
		if (cl == null) {
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Exception ex) {
					//	继续执行获取
				}
			}
		}
		return cl;
	}
	
	/**	
	 * 	根据指定包获取相对应Class
	 * 	<br>默认搜索多层结构
	 * 	
	 * 	@author Pan
	 * 	@param  packagePath	例如com.xxx.xx.x
	 * 	@return	List
	 */
	public static List<Class<?>> getPackageClasses(String packagePath) {
		return getPackageClasses(packagePath, true);
	}
	
	/**	
	 * 	根据指定包获取相对应Class
	 * 	<br>自定义搜索级别(单层、多层)
	 * 	
	 * 	@author Pan
	 * 	@param  packagePath		例如com.xxx.xx.x
	 * 	@param  multiLevel		true搜索多层级别,false搜索单层级别
	 * 	@return	List
	 */
	public static List<Class<?>> getPackageClasses(String packagePath, boolean multiLevel) {
		Assert.isNotEmpty(packagePath);
		
		List<Class<?>> list = CollUtils.newArrayList();
		//	转化包路径
		String reviseFilePath = FileUtils.reviseFilePath(StringUtils.replace(packagePath, ".", "/"));
		
		List<URL> listResources = ResourceUtils.getResources(reviseFilePath);
		for (int i = 0; i < listResources.size(); i++) {
			URL url = listResources.get(i);
			//	协议类型
			String protocol = url.getProtocol();
			
			if (protocol.endsWith(ResourceType.PROTOCOL_JAR.getValue())) {
				CollUtils.addAll(list, getClassesOfJarPath(url, reviseFilePath, multiLevel));
			}
			if (protocol.endsWith(ResourceType.PROTOCOL_FILE.getValue())) {
				CollUtils.addAll(list, getClassesOfFilePath(url.getFile(), packagePath, multiLevel));
			}
		}
		return list;
	}
	
	/**	
	 * 	对比类是否类型一致
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz1	类1
	 * 	@param 	clazz2	类2  
	 * 	@return	boolean
	 */
	public static boolean eq(Class<?>[] clazz1, Class<?>[] clazz2) {
	    if (clazz1 == null || clazz2 == null 
	    		|| clazz1.length != clazz2.length) {
	    	return false;
        }

        for (int i = 0, len = clazz1.length; i < len; i++) {
            if (!eq(clazz1[i], clazz2[i])) {
                return false;
            }
        }
        return true;
	}
	
	/**	
	 * 	对比类是否类型一致
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz1	类1
	 * 	@param 	clazz2	类2  
	 * 	@return	boolean
	 */
	public static boolean eq(Class<?> clazz1, Class<?> clazz2) {
		return clazz1 == clazz2;
	}
	
	/**	
	 * 	验证是否为全限定名
	 * 	
	 * 	@author Pan
	 * 	@param 	qualifiedName	全限定名
	 * 	@return	boolean
	 */
	public static boolean isFullyQualifiedClassName(String qualifiedName) {
	    if (ValidParam.isEmpty(qualifiedName) || !StringUtils.contains(qualifiedName, ".")) {
	        return false;
	    }
	    String[] parts = StringUtils.split(qualifiedName, ".");
	    
	    for (int i = 0; i < parts.length; i++) {
	    	 if (!isJavaIdentifier(parts[i])) {
	            return false;
	        }
		}
	    return true;
	}
	
	/**
	 * 	检查指定字符串是否为正确的JAVA标识符
	 * 		
	 * 	@author Pan
	 * 	@param 	identifier	标识符
	 * 	@return	boolean
	 */
	public static boolean isJavaIdentifier(String identifier) {
	    if (ValidParam.isEmpty(identifier) 
	    		|| !Character.isJavaIdentifierStart(identifier.charAt(0)) 
	    		|| identifier.length() > 64) {
	        return false;
	    }

	    for (int i = 1; i < identifier.length(); i++) {
	        if (!Character.isJavaIdentifierPart(identifier.charAt(i))) {
	            return false;
	        }
	    }
	    return true;
	}
	
	/**	
	 * 	forName方法
	 * 	<br>自带缓存
	 * 	<br>内部捕获forName异常
	 * 	
	 * 	@author Pan
	 * 	@param 	qualifiedName	全限定名
	 * 	@return	Class
	 */
	public static Class<?> forName(String qualifiedName) {
		return CLASS_CACHE.get(qualifiedName, () -> Class.forName(qualifiedName));
	}
	
	/**
	 * 	默认类加载器
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz	类
	 * 	@return	Class
	 */
	public static Class<?> load(Class<?> clazz) {
		return load(getName(clazz));
	}
	
	/**	
	 * 	默认类加载器
	 * 	<br>根据全限定名
	 * 	
	 * 	@author Pan
	 * 	@param 	qualifiedName	全限定名
	 * 	@return	Class
	 */
	public static Class<?> load(String qualifiedName) {
		return CLASS_CACHE.get(qualifiedName, () -> ClassLoader.getSystemClassLoader().loadClass(qualifiedName));
	}

	/**
	 * 	自定义线程加载器
	 * 	<br>根据全限定名
	 *
	 * 	@author Pan
	 * 	@param 	qualifiedName	全限定名
	 * 	@param 	classLoader		自定义ClassLoader
	 * 	@return	Class
	 */
	public static Class<?> load(String qualifiedName, ClassLoader classLoader) {
		return CLASS_CACHE.get(qualifiedName, () -> classLoader.loadClass(qualifiedName));
	}

	/**
	 * 	当前线程加载器
	 * 	<br>根据全限定名
	 *
	 * 	@author Pan
	 * 	@param 	qualifiedName	全限定名
	 * 	@return	Class
	 */
	public static Class<?> loadByCurrentThread(String qualifiedName) {
		return CLASS_CACHE.get(qualifiedName, () -> Thread.currentThread().getContextClassLoader().loadClass(qualifiedName));
	}
}
